﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SportsStore.Domain.Abstract;
using SportsStore.Domain.Entities;
using SportsStore.WebUI.Controllers;
using SportsStore.WebUI.Models;

namespace SportsStore.UnitTests
{
    [TestClass]
    public class CartControllerTests
    {
        [TestMethod]
        public void Index_WhenCalled_ShouldPoulateViewWithGivenCart()
        {
            // Arrange
            var cart = new Cart();
            cart.AddItem(new Product() { ProductID = 1 }, 1);
            
            var mockRepository = new Mock<IProductRepository>();
            var controller = new CartController(mockRepository.Object, null);

            // Act
            var result = controller.Index(cart, "Return1").Model as CartIndexViewModel;

            // Assert
            Assert.AreSame(cart, result.Cart, "Should use the given cart item.");
            Assert.AreEqual("Return1", result.ReturnUrl, "Should use the given return url.");
        }

        [TestMethod]
        public void AddToCart_WhenNoProducFoundInCart_ShouldAddToCart()
        {
            // Arrange
            var cart = new Cart();
            var mockRepository = new Mock<IProductRepository>();
            mockRepository.Setup(repo => repo.Products).Returns((new List<Product>() { new Product() { ProductID = 1 } }).AsQueryable());

            var controller = new CartController(mockRepository.Object, null);

            // Act
            var result = controller.AddToCart(cart, 1, "Return1");

            // Assert
            Assert.AreEqual(1, cart.Lines.Count(), "Should have added the item to repository.");
            Assert.AreEqual(1, cart.Lines.First().Product.ProductID, "Should have added the given product id.");
            Assert.AreEqual("Index", result.RouteValues["action"].ToString(), "Should direct to the Index action.");
            Assert.AreEqual("Return1", result.RouteValues["returnUrl"].ToString(), "Should direct to the Index action.");
        }

        [TestMethod]
        public void RemoveFromCart_WhenProductIsGive_ShouldRemoveFromCart()
        {
            // Arrange
            var cart = new Cart();
            cart.AddItem(new Product() { ProductID = 1 }, 1);
            var mockRepository = new Mock<IProductRepository>();
            var controller = new CartController(mockRepository.Object, null);

            // Act
            var result = controller.RemoveFromCart(cart, 1, "Return2");

            // Assert
            Assert.AreEqual(0, cart.Lines.Count(), "Should have removed from the cart.");
            Assert.AreEqual("Index", result.RouteValues["action"], "Should redirect to the the Index action.");
            Assert.AreEqual("Return2", result.RouteValues["returnUrl"], "Should used the given Url");
        }

        [TestMethod]
        public void Checkout_Cannot_Checkout_EmptyCart()
        {
            // Arrange
            var cart = new Cart();
            var shippingDetails = new ShippingDetails();

            var mockProcessor = new Mock<IOrderProcessor>();

            var controller = new CartController(null, mockProcessor.Object);

            // Act
            var result = controller.Checkout(cart, shippingDetails);

            // Assert
            Assert.IsFalse(controller.ModelState.IsValid, "Should be in the invalid state.");

            // Verify that process order has not be called.
            mockProcessor.Verify(processor => processor.ProcessOrder(It.IsAny<Cart>(), It.IsAny<ShippingDetails>()), Times.Never());

            Assert.AreEqual("", result.ViewName, "Should redirect to the same Checkout view.");
        }

        [TestMethod]
        public void Checkout_WhenCartContainsItems_ButShippingDetailsAreInValid_ShouldNotCheckout()
        {
            // Arrange
            var cart = new Cart();
            cart.AddItem(new Product() { ProductID = 10 }, 1);

            var shippingDetails = new ShippingDetails();

            var mockProcessor = new Mock<IOrderProcessor>();

            var controller = new CartController(null, mockProcessor.Object);
            controller.ModelState.AddModelError("test", "shipping error");

            // Act
            var result = controller.Checkout(cart, shippingDetails);

            // Assert
            mockProcessor.Verify(processor => processor.ProcessOrder(It.IsAny<Cart>(), It.IsAny<ShippingDetails>()), Times.Never(), "Should have never processed the order");
            Assert.AreEqual("", result.ViewName, "Should redirect to the same checkoutview");
        }

        [TestMethod]
        public void Checkout_WhenValidCartAndShippingDetailsArePresent_ShouldPlaceOrder()
        {
            // Arrange
            var cart = new Cart();
            cart.AddItem(new Product() { ProductID = 10 }, 1);

            var shippingDetails = new ShippingDetails() { Name = "M", Line1 = "Line1", City = "City", Country = "Country", State = "State", Zip = "Zip" };
            var mockProcessor = new Mock<IOrderProcessor>();

            var controller = new CartController(null, mockProcessor.Object);

            // Act
            var result = controller.Checkout(cart, shippingDetails);

            // Assert
            mockProcessor.Verify(process => process.ProcessOrder(cart, shippingDetails), Times.Once(), "Should process the order.");
            Assert.AreEqual("Completed", result.ViewName, "Should go to the appropriate Completed View.");
            Assert.IsTrue(controller.ModelState.IsValid, "Model should be valid.");
        }
    }
}
